/*
 * Copyright (C) 2021 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the
 *       distribution.
 *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <stdlib.h>
#include "gpio_xradio.h"
#include "device_resource_if.h"

#define HDF_LOG_TAG GPIO

/*dev api*/
static int32_t GpioDevWrite(struct GpioCntlr *cntlr, uint16_t gpio,
                uint16_t val)
{
    GPIO_Port port;
    GPIO_Pin pin;
    if (gpio >= GPIO_PIN_MAX) {
        HDF_LOGD("IoTGpioSetOutputVal gpio=%d error\r\n", gpio);
        return HDF_FAILURE;
    }

    port = (gpio > GPIOA_PIN_MAX) ? GPIO_PORT_B : GPIO_PORT_A;
    pin = (GPIO_Pin)((gpio > GPIOA_PIN_MAX) ? (gpio - GPIOA_PIN_MAX - 1) :
                          (gpio));
    HAL_GPIO_WritePin(port, pin, val ? GPIO_PIN_HIGH : GPIO_PIN_LOW);
    return HDF_SUCCESS;
}

static int32_t GpioDevRead(struct GpioCntlr *cntlr, uint16_t gpio,
               uint16_t *val)
{
    GPIO_Port port;
    GPIO_Pin pin;

    if (gpio >= GPIO_PIN_MAX) {
        HDF_LOGD("IoTGpioGetInputVal gpio=%d error\r\n", gpio);
        return HDF_FAILURE;
    }

    port = (gpio > GPIOA_PIN_MAX) ? GPIO_PORT_B : GPIO_PORT_A;
    pin = (GPIO_Pin)((gpio > GPIOA_PIN_MAX) ? (gpio - GPIOA_PIN_MAX - 1) :
                          (gpio));

    *val = (uint16_t)HAL_GPIO_ReadPin(port, pin);
    return HDF_SUCCESS;
}

static int32_t GpioDevSetDir(struct GpioCntlr *cntlr, uint16_t gpio,
                 uint16_t dir)
{
    GPIO_WorkMode mode;
    GPIO_Port port;
    GPIO_Pin pin;

    if (gpio >= GPIO_PIN_MAX) {
        HDF_LOGD("IoTGpioSetDir gpio=%d error\r\n", gpio);
        return HDF_FAILURE;
    }

    port = (gpio > GPIOA_PIN_MAX) ? GPIO_PORT_B : GPIO_PORT_A;
    pin = (GPIO_Pin)((gpio > GPIOA_PIN_MAX) ? (gpio - GPIOA_PIN_MAX - 1) :
                          (gpio));
    mode = dir ? GPIOx_Pn_F1_OUTPUT : GPIOx_Pn_F0_INPUT;
    HAL_GPIO_SetMode(port, pin, mode);
    return HDF_SUCCESS;
}

static int32_t GpioDevGetDir(struct GpioCntlr *cntlr, uint16_t gpio,
                 uint16_t *dir)
{
    GPIO_Port port;
    GPIO_Pin pin;
    GPIO_InitParam param;

    if (gpio >= GPIO_PIN_MAX) {
        HDF_LOGD("IoTGpioGetDir gpio=%d error\r\n", gpio);
        return HDF_FAILURE;
    }

    port = (gpio > GPIOA_PIN_MAX) ? GPIO_PORT_B : GPIO_PORT_A;
    pin = (GPIO_Pin)((gpio > GPIOA_PIN_MAX) ? (gpio - GPIOA_PIN_MAX - 1) :
                          (gpio));
    HAL_GPIO_GetConfig(port, pin, &param);
    *dir = param.mode;
    return HDF_SUCCESS;
}

static int32_t GpioDevSetIrq(struct GpioCntlr *cntlr, uint16_t gpio,
                 uint16_t mode, GpioIrqFunc func, void *arg)
{
    GPIO_Port port;
    GPIO_Pin pin;

    if (gpio >= GPIO_PIN_MAX) {
        HDF_LOGD("IoTGpioRegisterIsrFunc gpio=%d error\r\n", gpio);
        return HDF_FAILURE;
    }

    port = (gpio > GPIOA_PIN_MAX) ? GPIO_PORT_B : GPIO_PORT_A;
    pin = (GPIO_Pin)((gpio > GPIOA_PIN_MAX) ? (gpio - GPIOA_PIN_MAX - 1) :
                          (gpio));

    GPIO_IrqParam irq_param;
    irq_param.arg = arg;
    irq_param.callback = (GPIO_IRQCallback)func;
    if (mode == OSAL_IRQF_TRIGGER_RISING) {
        irq_param.event = GPIO_IRQ_EVT_RISING_EDGE;
    } else if (mode == OSAL_IRQF_TRIGGER_FALLING) {
        irq_param.event = GPIO_IRQ_EVT_FALLING_EDGE;
    } else if (mode == OSAL_IRQF_TRIGGER_HIGH) {
        irq_param.event = GPIO_IRQ_EVT_HIGH_LEVEL;
    } else if (mode == OSAL_IRQF_TRIGGER_LOW) {
        irq_param.event = GPIO_IRQ_EVT_LOW_LEVEL;
    }
    HAL_GPIO_EnableIRQ(port, pin, &irq_param);
    return HDF_SUCCESS;
}

static int32_t GpioDevUnSetIrq(struct GpioCntlr *cntlr, uint16_t gpio)
{
    GPIO_Port port;
    GPIO_Pin pin;

    if (gpio >= GPIO_PIN_MAX) {
        HDF_LOGD("IoTGpioUnregisterIsrFunc gpio=%d error\r\n", gpio);
        return HDF_FAILURE;
    }

    port = (gpio > GPIOA_PIN_MAX) ? GPIO_PORT_B : GPIO_PORT_A;
    pin = (GPIO_Pin)((gpio > GPIOA_PIN_MAX) ? (gpio - GPIOA_PIN_MAX - 1) :
                          (gpio));

    HAL_GPIO_DisableIRQ(port, pin);
    return HDF_SUCCESS;
}

static int32_t GpioDevEnableIrq(struct GpioCntlr *cntlr, uint16_t gpio)
{
    GPIO_Port port;
    GPIO_Pin pin;

    if (gpio >= GPIO_PIN_MAX) {
        HDF_LOGD("IoTGpioUnregisterIsrFunc gpio=%d error\r\n", gpio);
        return HDF_FAILURE;
    }

    port = (gpio > GPIOA_PIN_MAX) ? GPIO_PORT_B : GPIO_PORT_A;
    pin = (GPIO_Pin)((gpio > GPIOA_PIN_MAX) ? (gpio - GPIOA_PIN_MAX - 1) :
                          (gpio));

    GPIO_InitParam param;
    param.driving = GPIO_DRIVING_LEVEL_1;
    param.mode = GPIOx_Pn_F6_EINT;
    param.pull = GPIO_PULL_NONE;
    HAL_GPIO_Init(port, pin, &param);
    return HDF_SUCCESS;
}

static int32_t GpioDevDisableIrq(struct GpioCntlr *cntlr, uint16_t gpio)
{
    GPIO_Port port;
    GPIO_Pin pin;

    if (gpio >= GPIO_PIN_MAX) {
        HDF_LOGD("IoTGpioUnregisterIsrFunc gpio=%d error\r\n", gpio);
        return HDF_FAILURE;
    }

    port = (gpio > GPIOA_PIN_MAX) ? GPIO_PORT_B : GPIO_PORT_A;
    pin = (GPIO_Pin)((gpio > GPIOA_PIN_MAX) ? (gpio - GPIOA_PIN_MAX - 1) :
                          (gpio));

    GPIO_InitParam param;
    param.driving = GPIO_DRIVING_LEVEL_1;
    param.mode = GPIOx_Pn_F0_INPUT;
    param.pull = GPIO_PULL_NONE;
    HAL_GPIO_Init(port, pin, &param);
    return HDF_SUCCESS;
}
/* GpioMethod definitions */
struct GpioMethod g_GpioCntlrMethod = {
    .request = NULL,
    .release = NULL,
    .write = GpioDevWrite,
    .read = GpioDevRead,
    .setDir = GpioDevSetDir,
    .getDir = GpioDevGetDir,
    .toIrq = NULL,
    .setIrq = GpioDevSetIrq,
    .unsetIrq = GpioDevUnSetIrq,
    .enableIrq = GpioDevEnableIrq,
    .disableIrq = GpioDevDisableIrq,
};

static int32_t GpioDriverBind(struct HdfDeviceObject *device)
{
    if (device == NULL) {
        HDF_LOGD("Sample device object is null!\n");
        return -1;
    }
    static struct GpioCntlr gpioCntlr;
    gpioCntlr.device.hdfDev = device;
    device->service = (struct IDeviceIoService *)&(gpioCntlr.device);
    return HDF_SUCCESS;
}

static int32_t AttachGpioDevice(struct GpioCntlr *gpioCntlr,
                struct HdfDeviceObject *device)
{
    int32_t pinport, pinnum;
    GPIO_InitParam param;
    struct GpioDevice *gpioDevice = NULL;
    if (device->property == NULL) {
        HDF_LOGD("%s: property is NULL\n", __func__);
        return HDF_FAILURE;
    }

    struct DeviceResourceIface *dri =
        DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
    if (dri == NULL || dri->GetUint32 == NULL) {
        HDF_LOGD("DeviceResourceIface is invalid\n");
        return HDF_FAILURE;
    }
    dri->GetUint32(device->property, "pinport", &pinport, 0);
    dri->GetUint32(device->property, "pinnum", &pinnum, 0);
    dri->GetUint32(device->property, "driving", &param.driving, 0);
    dri->GetUint32(device->property, "mode", &param.mode, 0);
    dri->GetUint32(device->property, "pull", &param.pull, 0);
    HAL_GPIO_Init(pinport, pinnum, &param);

    return HDF_SUCCESS;
}

static int32_t GpioDriverInit(struct HdfDeviceObject *device)
{
    int32_t ret;
    struct GpioCntlr *gpioCntlr = NULL;

    HDF_LOGD("GPIO Init!\n", __func__);
    if (device == NULL) {
        HDF_LOGD("%s: device is NULL\n", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    if (device->service == NULL) {
        HDF_LOGD("%s: service is NULL\n", __func__);
    }

    gpioCntlr = GpioCntlrFromDevice(device);
    if (gpioCntlr == NULL) {
        HDF_LOGD("GpioCntlrFromDevice fail\n");
        return HDF_FAILURE;
    }

    ret = AttachGpioDevice(gpioCntlr,
                   device); // GpioCntlr add GpioDevice to priv
    if (ret != HDF_SUCCESS) {
        HDF_LOGD("AttachGpioDevice fail\n");
        return HDF_FAILURE;
    }

    gpioCntlr->count++;
    gpioCntlr->ops = &g_GpioCntlrMethod; // register callback
    ret = GpioCntlrAdd(gpioCntlr);

    if (ret != HDF_SUCCESS) {
        HDF_LOGD("ret = %d\r\n", ret);
        HDF_LOGD("GpioCntlrAdd fail %d\r\n", gpioCntlr->start);
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

static void GpioDriverRelease(struct HdfDeviceObject *device)
{
    HDF_LOGD("Sample driver release success\n");
    return;
}

struct HdfDriverEntry g_GpioDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "XRADIO_GPIO",
    .Bind = GpioDriverBind,
    .Init = GpioDriverInit,
    .Release = GpioDriverRelease,
};

HDF_INIT(g_GpioDriverEntry);
